using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Circus.CodeEditor;
using Circus.Syntax;
using Circus.Syntax.Lexer;

namespace Circus.CodeEditor {
    public class SyntaxPaint {
        protected Control control;
        private bool disableColorPaint;
        private bool disableSyntaxPaint;
        private Circus.Syntax.Lexer.Lexer lexer;
        protected Painter painter;

        public SyntaxPaint(Painter painter, Control control) {
            this.painter = painter;
            this.control = control;
        }

        [DefaultValue(false), Description("Gets or sets a value indicating whether control uses colors to paint its content.")]
        public virtual bool DisableColorPaint {
            get {
                return disableColorPaint;
            }
            set {
                if (disableColorPaint != value) {
                    disableColorPaint = value;
                    OnDisableColorPaintChanged();
                }
            }
        }

        [DefaultValue(false), Description("Gets or sets a value indicating whether Edit control uses lexical colors/styles to paint its content.")]
        public virtual bool DisableSyntaxPaint {
            get {
                return disableSyntaxPaint;
            }
            set {
                if (disableSyntaxPaint != value) {
                    disableSyntaxPaint = value;
                    OnDisableSyntaxPaintChanged();
                }
            }
        }

        [Description("Gets or sets object that can make lexical analysis for the control's content.")]
        public virtual Circus.Syntax.Lexer.Lexer Lexer {
            get {
                return lexer;
            }
            set {
                if (lexer != value) {
                    lexer = value;
                    OnLexerChaged();
                }
            }
        }

        private int MeasureTextFragment(string line, StringItemInfo style, int startChar, int endChar, int width, out int chars, bool measureChars, bool exact) {
            var textStyle = TextStyle.None;
            LexStyle lexStyle = GetLexStyle(style, ref textStyle);
            chars = 0;
            if (lexStyle != null && !disableSyntaxPaint) {
                painter.fontStyle = GetFontStyle(lexStyle.FontStyle, textStyle);
            } else {
                if (control != null) {
                    painter.fontStyle = GetFontStyle(control.Font.Style, textStyle);
                }
            }
            int result;
            if (measureChars) {
                result = painter.StringWidth(line, startChar, endChar - startChar + 1, width, out chars, exact);
            } else {
                result = painter.StringWidth(line, startChar, endChar - startChar + 1);
            }
            return result;
        }

        private int DrawTextFragment(Point position, string line, StringItemInfo style, int index, int startChar, int endChar) {
            var textStyle = TextStyle.None;
            LexStyle lexStyle = GetLexStyle(style, ref textStyle);
            if (disableSyntaxPaint) {
                lexStyle = null;
            }
            string text = (startChar == 0 && endChar == -1) ? line : line.Substring(startChar, endChar - startChar + 1);
            if (lexStyle != null) {
                painter.fontStyle = GetFontStyle(lexStyle.FontStyle, textStyle);
                painter.TextColor = GetFontColor(lexStyle.ForeColor, textStyle);
                if (lexStyle.BackColor != Color.Empty) {
                    painter.BackColor = lexStyle.BackColor;
                } else {
                    if (control != null) {
                        painter.BackColor = control.BackColor;
                    }
                }
            } else {
                if (control != null) {
                    painter.fontStyle = GetFontStyle(control.Font.Style, textStyle);
                    painter.TextColor = GetFontColor(control.ForeColor, textStyle);
                    painter.BackColor = control.BackColor;
                }
            }
            int num = painter.measureString(text);
            var rect = new Rectangle(position.X, position.Y, num, painter.FontHeight);
       
            painter.TextOut(text, -1, rect, true, false);
            
            return num;
        }

        protected virtual void GetString(int index, ref string text, ref StringItemInfo[] colorData) {}

        protected virtual bool EqualStates(TextStyle state1, TextStyle state2) {
            return state1 == state2;
        }

        protected virtual bool EqualLineStyles(EditLineStyle style1, EditLineStyle style2, StringItemInfo style, StringItemInfo oldStyle) {
            bool result;
            if (style1 == style2) {
                result = true;
            } else {
                var textStyle = TextStyle.None;
                var textStyle2 = TextStyle.None;
                GetLexStyle(style, ref textStyle);
                GetLexStyle(style, ref textStyle2);
                result = ((byte) (textStyle & TextStyle.OutlineSection) != 0 || (byte) (textStyle & TextStyle.CodeSnippet) != 0 || (byte) (textStyle2 & TextStyle.OutlineSection) != 0 || (byte) (textStyle2 & TextStyle.CodeSnippet) != 0);
            }
            return result;
        }

        public virtual FontStyle GetFontStyle(FontStyle fontStyle, TextStyle textStyle) {
            return fontStyle;
        }

        public virtual Color GetFontColor(Color color, TextStyle textStyle) {
            return (disableColorPaint && control != null) ? control.ForeColor : ((color != Color.Empty) ? color : painter.TextColor);
        }

        public virtual LexStyle GetLexStyle(StringItemInfo style, ref TextStyle textStyle) {
            LexStyle result;
            if (style.Data == 255) {
                result = null;
            } else {
                byte data = style.Data;
                textStyle = style.TextStyle;
                if (data == 0 || Lexer == null) {
                    result = null;
                } else {
                    result = ((data > 0 && data - 1 < Lexer.Scheme.Styles.Count) ? Lexer.Scheme.Styles[data - 1] : null);
                }
            }
            return result;
        }

        public virtual Color GetBackColor(Color color, TextStyle state) {
            return (color != Color.Empty) ? color : painter.BackColor;
        }

        public bool EqualStyles(StringItemInfo style1, StringItemInfo style2, bool useColors) {
            var state = TextStyle.None;
            var state2 = TextStyle.None;
            LexStyle lexStyle = GetLexStyle(style1, ref state);
            LexStyle lexStyle2 = GetLexStyle(style2, ref state2);
            if (disableSyntaxPaint) {
                lexStyle = null;
                lexStyle2 = null;
            }
            return EqualStates(state, state2) && (lexStyle == lexStyle2 || (lexStyle != null && lexStyle2 != null && lexStyle.FontStyle == lexStyle2.FontStyle && (!useColors || disableColorPaint || (lexStyle.ForeColor == lexStyle2.ForeColor && lexStyle.BackColor == lexStyle2.BackColor))));
        }

        protected virtual void OnDisableColorPaintChanged() {}
        protected virtual void OnDisableSyntaxPaintChanged() {}
        protected virtual void OnLexerChaged() {}
        protected virtual void OnBackColorChanged() {}

        protected virtual bool NeedParse() {
            return Lexer != null;
        }

        public virtual void PaintLineBookMarks(Painter painter, Rectangle rect) {}

        public virtual void PaintSyntax(Painter painter, int startLine, int endLine, Point position, Rectangle rect, Point location, bool specialPaint) {
            int fontHeight = painter.FontHeight;
            for (int i = startLine; i < endLine; i++) {
                DrawLine(i, position, rect);
                position.Y += fontHeight;
                if (position.Y > rect.Bottom || (position.Y == rect.Bottom && specialPaint)) {
                    break;
                }
            }
        }

        public virtual void DrawLine(int index, Point position, Rectangle clipRect) {
            string empty = string.Empty;
            StringItemInfo[] colorData = null;
            GetString(index, ref empty, ref colorData);
            DrawLine(index, empty, colorData, position, clipRect);
        }

        public virtual void DrawLine(int index, string line, StringItemInfo[] colorData, Point position, Rectangle clipRect) {
            int right = clipRect.Right;
            int length = line.Length;
            if (line != string.Empty) {
                StringItemInfo stringItemInfo = colorData[0];
                StringItemInfo stringItemInfo2 = stringItemInfo;
                int num = 0;
                for (int i = 1; i < length; i++) {
                    stringItemInfo = colorData[i];
                    if (!EqualStyles(stringItemInfo2, stringItemInfo, true)) {
                        position.X += DrawTextFragment(position, line, stringItemInfo2, index, num, i - 1);
                        if (position.X >= right) {
                            break;
                        }
                        stringItemInfo2 = stringItemInfo;
                        num = i;
                    }
                }
                if (num < length && position.X < right) {
                    position.X += DrawTextFragment(position, line, stringItemInfo2, index, num, length - 1);
                }
            }
        }

        public virtual int MeasureLine(int index, int pos, int len) {
            string empty = string.Empty;
            StringItemInfo[] colorData = null;
            GetString(index, ref empty, ref colorData);
            int num;
            return MeasureLine(empty, colorData, pos, len, -1, out num, false, false, false);
        }

        public virtual int MeasureLine(int index, int pos, int len, int width, out int chars, bool exact) {
            string empty = string.Empty;
            StringItemInfo[] colorData = null;
            GetString(index, ref empty, ref colorData);
            return MeasureLine(empty, colorData, pos, len, width, out chars, true, true, exact);
        }

        public virtual int MeasureLine(string line, StringItemInfo[] colorData, int pos, int len) {
            int num;
            return MeasureLine(line, colorData, pos, len, -1, out num, false, false, false);
        }

        public virtual int MeasureLine(string line, StringItemInfo[] colorData, int pos, int len, int width, out int chars, bool measureChars, bool addSpace, bool exact) {
            chars = 0;
            int result;
            if (measureChars) {
                if (width == int.MaxValue) {
                    chars = int.MaxValue;
                    result = int.MaxValue;
                    return result;
                }
                if (len == int.MaxValue) {
                    result = int.MaxValue;
                    return result;
                }
            } else {
                if (len == int.MaxValue) {
                    result = int.MaxValue;
                    return result;
                }
            }
            if (line == string.Empty) {
                if (!measureChars) {
                    result = painter.CharWidth(' ', len);
                } else {
                    result = painter.CharWidth(' ', width, out chars);
                }
            } else {
                int num = 0;
                int num2 = 0;
                int num3 = (len < 0) ? (line.Length - pos) : Math.Min(len, line.Length - pos);
                if ((!NeedParse() && false) || colorData == null) {
                    if (!measureChars) {
                        if (num3 >= 0) {
                            num = painter.StringWidth(line, 0, num3);
                        } else {
                            num = painter.measureString(line);
                        }
                        if (len > num3) {
                            num += painter.CharWidth(' ', len - num3);
                        }
                        result = num;
                        return result;
                    }
                    num = painter.StringWidth(line, pos, -1, width, out chars, exact);
                } else {
                    StringItemInfo stringItemInfo = colorData[0];
                    StringItemInfo stringItemInfo2 = stringItemInfo;
                    int num4 = pos;
                    for (int i = pos + 1; i < pos + num3; i++) {
                        stringItemInfo = colorData[i];
                        if (!stringItemInfo.Equals(stringItemInfo2) && !EqualStyles(stringItemInfo2, stringItemInfo, false)) {
                            num += MeasureTextFragment(line, stringItemInfo2, num4, i - 1, width - num, out num2, measureChars, exact);
                            chars += num2;
                            stringItemInfo2 = stringItemInfo;
                            if (measureChars && (width < num || num2 < i - num4)) {
                                width = -1;
                                break;
                            }
                            num4 = i;
                        }
                    }
                    if (num4 < pos + num3 && ((measureChars && width > num) || !measureChars)) {
                        num += MeasureTextFragment(line, stringItemInfo2, num4, pos + num3 - 1, width - num, out num2, measureChars, exact);
                        chars += num2;
                        if (measureChars && num2 < pos + num3 - num4) {
                            width = -1;
                        }
                    }
                    if (!measureChars && len > num3) {
                        var textStyle = TextStyle.None;
                        LexStyle lexStyle = GetLexStyle(stringItemInfo2, ref textStyle);
                        if (lexStyle != null) {
                            painter.fontStyle = GetFontStyle(lexStyle.FontStyle, textStyle);
                            num += painter.CharWidth(' ', len - num3);
                        } else {
                            if (control != null) {
                                painter.fontStyle = GetFontStyle(control.Font.Style, textStyle);
                            }
                            num += painter.CharWidth(' ', len - num3);
                        }
                    }
                }
                if (measureChars && addSpace) {
                    if (width > num) {
                        num += painter.CharWidth(' ', width - num, out num2);
                        chars += num2;
                    }
                }
                result = num;
            }
            return result;
        }

        public int MeasureLine(string line, StringItemInfo[] colorData, int pos, int len, int width, out int chars, bool exact) {
            return MeasureLine(line, colorData, pos, len, width, out chars, true, true, exact);
        }
    }
}